#include <windows.h>
#include "Button.h"
#include "resource.h"

#define WM_ENDGAME 0x7fff

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

const char szClassName[ ] = "WindowsApp";
const char* szWinLose[2][2]={TEXT("Lose"),
				TEXT("Mine is clicked! Come on next time.\n Would you like to continue?"),
				TEXT("Winning"),
				TEXT("You've make it.\n Would you like to try another map?")
				};

HWND hWnd;
Button btn[18][32];
POINT lt={0, 0};
bool isLDown=0, isRDown=0, isFirst=1;
rank srk={10, 10, 10};
int bSuccess=0, leave=srk.TotalMine;
int Hour=0, Minute=0, Second=0;

void AssignMiner(int, int);
bool InRange(int, int);

void LeftRightDown(HDC, int, int, bool);
int LeftRightUp(HDC, int, int);
int LeftUp(HDC, int, int);
void ShowMinerLeft(HDC hdc, RECT& rect);
void ShowTime(HDC, RECT&);

void AnotherGame();

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)

{
    MSG messages;
    WNDCLASSEX wincl;
	HMENU hMenu;

    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof (WNDCLASSEX);

    wincl.hIcon = LoadIcon (hThisInstance, MAKEINTRESOURCE(IDI_ICON));
    wincl.hIconSm = LoadIcon (hThisInstance, MAKEINTRESOURCE(IDI_ICON));
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) (COLOR_BTNFACE+1);

	hMenu=LoadMenu(hThisInstance, MAKEINTRESOURCE(IDR_MENU1));

    if (!RegisterClassEx (&wincl))
        return 0;

    hWnd = CreateWindowEx (
           0,
           szClassName,
           "MineSweeper",
           WS_OVERLAPPEDWINDOW^WS_MAXIMIZEBOX ,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           800,
		   500,
           HWND_DESKTOP,
           hMenu,
           hThisInstance,
           NULL
           );

    ShowWindow (hWnd, nFunsterStil);
	UpdateWindow(hWnd);
	srand(GetTickCount());

    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }

    return messages.wParam;
}

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	static int i, j;
	PAINTSTRUCT ps;
	static RECT rect1, rect2, rect3;
	char remaintime[30];

    switch (message)
    {
	case WM_SIZE:
		InvalidateRect(hwnd, NULL, TRUE);
	break;

	case WM_PAINT:
		GetClientRect(hwnd, &rect1);
		lt.x=(rect1.right-rect1.left-Button::BtnSize*srk.wide)/2;
		lt.y=(rect1.bottom-rect1.top-Button::BtnSize*srk.high-25)/2;
		rect3.top=rect2.top=rect1.bottom-25;
		rect2.left=(rect1.right-rect1.left)*2/3;
		rect2.right=rect2.left+150;
		rect3.bottom=rect2.bottom=rect1.bottom;
		rect3.left=(rect1.right-rect1.left)/4;
		rect3.right=rect3.left+100;

		hdc=BeginPaint(hwnd, &ps);
		for (i=0;i<srk.high;i++)
		for (j=0;j<srk.wide;j++)
			btn[i][j].DrawBtn(hdc, i, j, lt);
		DrawText(hdc, remaintime, sprintf(remaintime, "Time %02d:%02d:%02d", Hour, Minute, Second),
			&rect3, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
		ShowMinerLeft(hdc, rect2);
		EndPaint(hwnd, &ps);
	break;

	case WM_LBUTTONDOWN:
		i=(HIWORD(lParam)-lt.y)/25, j=(LOWORD(lParam)-lt.x)/25;
		if (InRange(i, j))
		{
			hdc=GetDC(hwnd);
			isLDown=true;
			if (isRDown)
			{
				LeftRightDown(hdc, i, j, 1);
			}
			else
				btn[i][j].IsPushing(hdc, i, j, lt, 1);
			ReleaseDC(hwnd, hdc);
		}
	break;

	case WM_MOUSEMOVE:
		hdc=GetDC(hwnd);
		if (isLDown)
		{
			if (isRDown)
			{
				if (InRange(i, j))LeftRightDown(hdc, i, j, 0);
				i=(HIWORD(lParam)-lt.y)/25;j=(LOWORD(lParam)-lt.x)/25;
				if (InRange(i, j))LeftRightDown(hdc, i, j, 1);
			}
			else
			{
				if (InRange(i, j))btn[i][j].IsPushing(hdc, i, j, lt, 0);
				i=(HIWORD(lParam)-lt.y)/25;j=(LOWORD(lParam)-lt.x)/25;
				if (InRange(i, j))btn[i][j].IsPushing(hdc, i, j, lt, 1);
			}
		}
		ReleaseDC(hwnd, hdc);
	break;

	case WM_LBUTTONUP:
		i=(HIWORD(lParam)-lt.y)/25, j=(LOWORD(lParam)-lt.x)/25;
		isLDown=false;
		if (InRange(i, j))
		{
			hdc=GetDC(hwnd);
			if (isFirst)
			{
				AssignMiner(i, j);
				SetTimer(hwnd, 1, 1000, NULL);
				isFirst=false;
			}
			if (isRDown)
				SendMessage(hwnd, WM_ENDGAME, LeftRightUp(hdc, i, j), 0);
			else
				SendMessage(hwnd, WM_ENDGAME, LeftUp(hdc, i, j), 0);
			ReleaseDC(hwnd, hdc);
		}
	break;

	case WM_RBUTTONDOWN:
		i=(HIWORD(lParam)-lt.y)/25;j=(LOWORD(lParam)-lt.x)/25;
		if (InRange(i, j))
		{
			hdc=GetDC(hwnd);
			isRDown=true;
			if (isLDown)
				LeftRightDown(hdc, i, j, 1);
			else
			{
				switch (btn[i][j].state)
				{
				case NOT_PUSH:
					btn[i][j].state=IS_FLAG;
					leave--;
				break;

				case IS_FLAG:
					btn[i][j].state=UNKNOWN;
					leave++;
				break;
				case UNKNOWN:
					btn[i][j].state=NOT_PUSH;
				break;
				}
				btn[i][j].DrawBtn(hdc, i, j, lt);
				ShowMinerLeft(hdc, rect2);
			}
			ReleaseDC(hwnd, hdc);
		}
	break;

	case WM_RBUTTONUP:
		i=(HIWORD(lParam)-lt.y)/25;j=(LOWORD(lParam)-lt.x)/25;
		isRDown=false;
		if (InRange(i, j))
		{
			hdc=GetDC(hwnd);
			if (isLDown)
				SendMessage(hwnd, WM_ENDGAME, LeftRightUp(hdc, i, j), 0);
			ReleaseDC(hwnd, hdc);
		}
	break;

	case WM_COMMAND:
		switch (wParam)
		{
		case ID_GAMES_NEWGAME:
			if (!isFirst)KillTimer(hwnd, 1);
			AnotherGame();
		break;

		case ID_GAMES_EXIT:
			SendMessage(hwnd, WM_CLOSE, 0, 0);
		break;

		case ID_OPTIONS_PRIMIARY:
			if (!isFirst)KillTimer(hwnd, 1);
			srk.wide=srk.high=9;
			srk.TotalMine=10;
			AnotherGame();
		break;

		case ID_OPTIONS_MEDIUM:
			if (!isFirst)KillTimer(hwnd, 1);
			srk.wide=srk.high=16;
			srk.TotalMine=40;
			AnotherGame();
		break;

		case ID_OPTIONS_HARD:
			if (!isFirst)KillTimer(hwnd, 1);
			srk.wide=30;
			srk.high=16;
			srk.TotalMine=99;
			AnotherGame();
		break;
		}
	break;

	case WM_ENDGAME:
		int test;
		if (wParam!=2)
		{
			KillTimer(hwnd, 1);
			test=MessageBox(hwnd, szWinLose[wParam][1], szWinLose[wParam][0], MB_YESNO);
			if (test==IDYES)
				AnotherGame();
			else SendMessage(hwnd, WM_CLOSE, 0, 0);
		}
	break;

	case WM_TIMER:
		hdc=GetDC(hwnd);
		ShowTime(hdc, rect3);
		ReleaseDC(hwnd, hdc);
	break;

	case WM_DESTROY:
		PostQuitMessage (0);
	break;

	default:
        return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return 0;
}

void AssignMiner(int rx, int ry)
{
	int i, j;

	for (i=0;i<srk.TotalMine;)
	{
		int M=rand()%(srk.high*srk.wide);
		int tmpx=M/srk.wide, tmpy=M%srk.wide;
		if (tmpx!=rx&&tmpy!=ry&&btn[tmpx+1][tmpy+1].MineCnt!=9)
		{
			btn[tmpx+1][tmpy+1].MineCnt=9;i++;
		}
	}

	for (i=1;i<=srk.high;i++)
		for (j=1;j<=srk.wide;j++)
		{
			int cnt=0;
			if (btn[i][j].MineCnt==9)cnt=9;
			else
			{
				for (int s1=-1;s1<2;s1++)
					for (int s2=-1;s2<2;s2++)
					{
						if (btn[i+s1][j+s2].MineCnt==9)cnt++;
					}
			}
			btn[i-1][j-1].MineCnt=cnt;
		}
}

bool InRange(int indx, int indy)
{
	return !(indx<0||indx>=srk.high||indy<0||indy>=srk.wide);
}

void LeftRightDown(HDC hdc, int indx, int indy, bool state)
{
	for (int i=-1;i<2;i++)
		for (int j=-1;j<2;j++)
		{
			if (InRange(indx+i, indy+j))
			{
				btn[indx+i][indy+j].IsPushing(hdc, indx+i, indy+j, lt, state);
			}
		}
}

int LeftRightUp(HDC hdc, int i, int j)
{
	int cnt=0;
	for (int s1=-1;s1<2;s1++)
	for (int s2=-1;s2<2;s2++)
	{
		if (InRange(i+s1, j+s2))
		{
			if (btn[i+s1][j+s2].state==IS_FLAG)cnt++;
		}
	}

	if (cnt==btn[i][j].MineCnt&&btn[i][j].state==IS_PUSH)
	{
		for (int s1=-1;s1<2;s1++)
		for (int s2=-1;s2<2;s2++)
		{
			int ret=LeftUp(hdc, i+s1, j+s2);
			if (ret!=2)return ret;
		}
	}

	for (int s1=-1;s1<2;s1++)
	for (int s2=-1;s2<2;s2++)
	{
		if (InRange(i+s1, j+s2)&&(btn[i+s1][j+s2].state!=IS_PUSH))
			btn[i+s1][j+s2].IsPushing(hdc, i+s1, j+s2, lt, 0);
	}
	return 2;
}

int LeftUp(HDC hdc, int i, int j)
{
    if (btn[i][j].state==UNKNOWN) btn[i][j].state=NOT_PUSH;

	if (!InRange(i, j)||btn[i][j].state) return 2;

	btn[i][j].IsLClicked(hdc, i, j, lt);
	if (btn[i][j].MineCnt!=9)bSuccess++;
	if (bSuccess==(srk.wide*srk.high-srk.TotalMine))
		return 1;

	switch (btn[i][j].MineCnt)
	{
	case 0:
		for (int s1=-1;s1<2;s1++)
		for (int s2=-1;s2<2;s2++)
			if (s1!=0||s2!=0)
			{
				int s=LeftUp(hdc, i+s1, j+s2);
				if (s!=2) return s;
			}
	break;

	case 9:
		return 0;
	}
	return 2;
}

void AnotherGame()
{
	for (int i=0;i<18;i++)
	for (int j=0;j<32;j++)
		btn[i][j].Reset();

	bSuccess=0;
	leave=srk.TotalMine;
	isLDown=isRDown=0;
	isFirst=1;
	Hour=Minute=Second=0;

	InvalidateRect(hWnd, NULL, TRUE);
}

void ShowMinerLeft(HDC hdc, RECT& rect)
{
	char s[30];
	SetBkColor(hdc, RGB(255, 255, 255));
	SetTextColor(hdc, RGB(0, 0, 255));
	FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
	DrawText(hdc, s, sprintf(s, "Miner Remain: %d", leave),
		&rect, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
}

void ShowTime(HDC hdc, RECT& rect)
{
	int carry;
	carry=(++Second)/60;
	Second%=60;
	Minute+=carry;
	carry=Minute/60;
	Minute%=60;
	Hour+=carry;

	char s[20];
	SetBkColor(hdc, RGB(255, 255, 255));
	SetTextColor(hdc, RGB(0, 0, 255));
	FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
	DrawText(hdc, s, sprintf(s, "Time %02d:%02d:%02d", Hour, Minute, Second),
		&rect, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
}

